package org.anderground.editor.edits;

import java.util.Arrays;
import java.util.List;

import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.UndoableEdit;

import org.anderground.core.graphics.PointF;
import org.anderground.core.model.Station;
import org.anderground.core.util.Coord;

/**
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
public abstract class MoveStationCoordsAction extends Action {

	private float dx, dy;
	private Station[] stations;

	/**
	 * 
	 * @param dx
	 * @param dy
	 * @param stations
	 */
	public MoveStationCoordsAction(float dx, float dy, Station... stations) {
		this.dx = dx;
		this.dy = dy;
		this.stations = stations;
	}

	/**
	 * 
	 * @param dx
	 * @param dy
	 * @param coords
	 */
	public MoveStationCoordsAction(float dx, float dy, List<? extends Station> stations) {
		this(dx, dy, stations == null ? new Station[] {} : (Station[]) stations
				.toArray(new Station[stations.size()]));
	}

	@Override
	UndoableEdit perform() throws ActionException {
		return new CoordinatesEdit(dx, dy, stations);
	}

	protected abstract Coord getCoord(Station station);

	protected abstract void setCoord(Station station, Coord coord);

	/**
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	@SuppressWarnings("serial")
	private class CoordinatesEdit extends AbstractUndoableEdit {

		/** the stations to move */
		private Station[] stations;
		private PointF[] pointFs;
		private PointF delta;

		/**
		 * 
		 * @param dx
		 * @param dy
		 * @param coords
		 */
		public CoordinatesEdit(float dx, float dy, Station[] stations) {
			delta = new PointF(dx, dy);
			this.stations = stations;
			pointFs = new PointF[stations.length];
			for (int i = 0; i < stations.length; i++) {
				pointFs[i] = new PointF(getCoord(stations[i]));
			}
			shift(delta);
		}

		@Override
		public void undo() {
			shift(-delta.x, -delta.y);
		}

		@Override
		public void redo() {
			shift(delta);
		}

		@Override
		public boolean addEdit(UndoableEdit anEdit) {
			if (anEdit instanceof CoordinatesEdit) {
				CoordinatesEdit coordEdit = (CoordinatesEdit) anEdit;
				if (Arrays.equals(stations, coordEdit.stations)) {
					delta.add(coordEdit.delta);
					shift(coordEdit.delta);
					return true;
				}
			}
			return false;
		}

		private void shift(PointF delta) {
			shift(delta.x, delta.y);
		}

		private void shift(float dx, float dy) {
			for (int i = 0; i < stations.length; i++) {
				pointFs[i].x += dx;
				pointFs[i].y += dy;
				setCoord(stations[i], new Coord((int) pointFs[i].x,
						(int) pointFs[i].y));
			}
		}

	}

}
